#include <dialects/Builtin/Builtin.h>
#include <dialects/Func/Func.h>
#include <dialects/PL/PL.h>
#include <gtest/gtest.h>
#include <lcir/ADT/IList.h>
#include <lcir/ADT/ListNode.h>
#include <lcir/ADT/Range.h>
#include <lcir/IR/Argument.h>
#include <lcir/IR/Constant.h>
#include <lcir/IR/Context.h>
#include <lcir/IR/ExprTemps.h>
#include <lcir/IR/Expression.h>
#include <lcir/IR/IRBuilder.h>
#include <lcir/IR/Lambda.h>
#include <lcir/IR/ValuePrinter.h>

#include <iterator>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include "dialects/Builtin/Prototype.h"
#include "dialects/Builtin/Types.h"
#include "dialects/PL/Ops.h"
#include "dialects/PL/Translation/ToCFG.h"
#include "dialects/PL/Translation/ToMath.h"

using namespace lcir;
using namespace lcir::builtin;
using namespace lcir::func;
using namespace lcir::pl;

static void buildLambda(Lambda *lambda) {
  IRBuilder builder(lambda);
  auto *x = builder.createAndGetRet<DeclVar>(Int32Type::get(), "x");
  auto *y = builder.createAndGetRet<DeclVar>(Int32Type::get(), "y");
  auto *z = builder.createAndGetRet<DeclVar>(Int32Type::get(), "z");
  auto *add1 = builder.createAndGetRet<pl::Add>({x, y});
  auto *add2 = builder.createAndGetRet<pl::Add>({x, z});
  builder.createAndGetRet<Mul>({add1, add2});
}
static void buildMathLambda(Lambda *lambda) {
  IRBuilder builder(lambda);
  auto func = Lambda::get({}, Int32Type::get());
  builder.create<Func>({func}, "main");
  builder.setInsertPoint(func);
  auto *x = builder.createAndGetRet<DeclVar>(Int32Type::get(), "x");
  auto *y = builder.createAndGetRet<DeclVar>(Int32Type::get(), "y");
  auto *z = builder.createAndGetRet<DeclVar>(FloatType::get(), "z");
  auto *add1 = builder.createAndGetRet<pl::Add>({x, y});
  auto *add2 = builder.createAndGetRet<pl::Add>({x, z});
  auto *mul = builder.createAndGetRet<Mul>({add1, add2});
  auto ret = builder.createAndGetRet<pl::Assign>({mul, x});
  builder.create<func::Return>({x});
}
class Pl: public testing::Test {
 protected:
  void SetUp() override {
    // 初始化lambda
    module = Lambda::get();
    IRBuilder b(module);
    b.setInsertPoint(module);
    auto func = Lambda::get();
    // buildLambda(func);
    b.create<Func>({func}, "main");
    b.setInsertPoint(func);
    auto [iflambda, ellambda] = buildIfEl(b);
    b.setInsertPoint(iflambda);
    buildIfEl(b);
    b.setInsertPoint(func);
    b.create<pl::DeclVar>(Int32Type::get(), "GG");
  }
  std::pair<Lambda*, Lambda*> buildIfEl(IRBuilder b) {
    auto *ifLambda = Lambda::get();
    auto *elLambda = Lambda::get();
    auto c0 = Constant::get(std::make_unique<Int32ConstProto>(1));
    auto c1 = Constant::get(std::make_unique<Int32ConstProto>(2));
    // auto c2 = Constant::get(std::make_unique<Int32ConstProto>(3));
    // auto c3 = Constant::get(std::make_unique<Int32ConstProto>(4));
    auto *l0 = b.createAndGetRet<EQ>({c0, c1});
    // auto *l1 = b.createAndGetRet<GT>({c0, c1});
    // auto *l2 = b.createAndGetRet<LT>({c2, c3});
    // auto *l3 = b.createAndGetRet<EQ>({c2, c3});
    // auto *or0 = b.createAndGetRet<Or>({l0, l1});
    // auto *or1 = b.createAndGetRet<Or>({l2, l3});
    // auto *and0 = b.createAndGetRet<And>({or0, or1});
    // b.setInsertPoint(module);
    auto *ifCond = b.createAndGetRet<If>({l0, ifLambda});
    b.create<El>({ifCond, elLambda});
    b.setInsertPoint(ifLambda);
    b.create<pl::DeclVar>(Int32Type::get(), "x");
    b.setInsertPoint(elLambda);
    b.create<pl::DeclVar>(Int32Type::get(), "y");
    b.setInsertPoint(ifLambda);
    return {ifLambda, elLambda};
  }
  Lambda *module{nullptr};

  Expression *ret{nullptr};
};
TEST_F(Pl, Pl2CFG) {
  pl::ToCFG pass;
  pass.setLambda(module);
  pass.run();
}

// TEST_F(Pl, Pl2MATH) {
//   pl::ToMath pass;
//   auto module = Lambda::get();
//   buildMathLambda(module);
//   pass.setLambda(module);
//   pass.run();
// }


